/*
 * procfs2.c -  create a "file" in /proc
 */

#include <linux/kernel.h> /* We're doing kernel work */ 
#include <linux/module.h> /* Specifically, a module */ 
#include <linux/proc_fs.h> /* Necessary because we use the proc fs */ 
#include <linux/uaccess.h> /* for copy_from_user */ 
#include <linux/version.h> 

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) 
#define HAVE_PROC_OPS 
#endif 

#define PROCFS_MAX_SIZE 1024 
#define PROCFS_NAME "buffer1k" 

 /* This structure hold information about the /proc file */
static struct proc_dir_entry* our_proc_file;

/* The buffer used to store character for this module */
static char procfs_buffer[PROCFS_MAX_SIZE];

/* The size of the buffer */
static unsigned long procfs_buffer_size = 0;

/* This function is called then the /proc file is read */
static ssize_t procfile_read(struct file* fp, char __user* buffer,
    size_t length, loff_t* offset)
{
    int len = PROCFS_MAX_SIZE;
    ssize_t ret = len;
    int remain_len = len - *offset;
    pr_info("11 copy to user, len=%ld, off=%lld\n", length, *offset);

    if (remain_len > 0) {
        if (remain_len >= length)
            len = length;
        else
            len = remain_len;

        pr_info("12 real read, len=%d\n", len);
        if (copy_to_user(buffer, procfs_buffer + *offset, len) == 0) {
            ret = len;
            *offset += len;
            pr_info("procfile read %s\n", fp->f_path.dentry->d_name.name);
        }
    }
    else {
        pr_info("copy_to_user failed, remain_len=%d\n", remain_len);
        ret = 0;
    }

    pr_info("ret=%ld\n", ret);

    return ret;
}

/* This function is called with the /proc file is written. */
static ssize_t procfile_write(struct file* file, const char __user* buff,
    size_t length, loff_t* off)
{
    int len = sizeof(procfs_buffer);
    int remain_len;
    remain_len = len - *off;

    if (remain_len > 0)
    {
        if (remain_len >= length)
            len = length;
        else
            len = remain_len;

        pr_info("22 copy from user,  len=%ld, real_len=%d, off=%lld\n", length, len, *off);
        if (copy_from_user(procfs_buffer + *off, buff, len)) {
            return -EFAULT;
        }

        *off += len;
    }
    else
        return 0;

    pr_info("procfile write \n");

    return len;
}

#ifdef HAVE_PROC_OPS 
static const struct proc_ops proc_file_fops = {
    .proc_read = procfile_read,
    .proc_write = procfile_write,
};
#else 
static const struct file_operations proc_file_fops = {
    .read = procfile_read,
    .write = procfile_write,
};
#endif 

static void init_procfs_buffer(char buf[], unsigned int len)
{
    // 初始化为可视化字符 !~/,0~9,A~Z,a~z
    int rang = 'z' - '!';
    int i;
    for (i = 0;i < len;i++) {
        buf[i] = i % rang + '!';
    }
}

static int __init procfs2_init(void)
{
    init_procfs_buffer(procfs_buffer, sizeof(procfs_buffer));
    our_proc_file = proc_create(PROCFS_NAME, 0666, NULL, &proc_file_fops);
    if (NULL == our_proc_file) {
        pr_alert("Error:Could not initialize /proc/%s\n", PROCFS_NAME);
        return -ENOMEM;
    }

    pr_info("/proc/%s created\n", PROCFS_NAME);
    return 0;
}

static void __exit procfs2_exit(void)
{
    proc_remove(our_proc_file);
    pr_info("/proc/%s removed\n", PROCFS_NAME);
}

module_init(procfs2_init);
module_exit(procfs2_exit);

MODULE_LICENSE("GPL");